/*
 ============================================================================
 Name        : TP1.c
 Author      : guido
 Version     :
 Copyright   : Your copyright notice
 Description :
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct campoDefine {
	char nombre[100];
	char valor[100];
};

typedef struct campoDefine campoDefine;

const int longitud = 80;
const int limiteDefine = 200;
const int cantidadDeDefines = 50;
const char saltoDeLinea = 10;
const char asterisco = '*';
const char cierreDeComentario[3] = { '*', '/', 0 };
const char caracteresDeSeparacion[13] = { ' ', '*', '+', '-', '/', '%', '<', '>', '=', '(', ')', 10, 0 };
const char finDeLinea = '\n';

const char include[10] = { '#', 'i', 'n', 'c', 'l', 'u', 'd', 'e', ' ', 0 };
const int longInclude = 9;
const char define[9] = { '#', 'd', 'e', 'f', 'i', 'n', 'e', ' ', 0 };
const int longDefine = 8;
const char ifndef[9] = { '#', 'i', 'f', 'n', 'd', 'e', 'f', ' ', 0 };
const int longIfndef = 8;
const char ifdef[8] = { '#', 'i', 'f', 'd', 'e', 'f', ' ', 0 };
const int longIfdef = 7;
const char endif[8] = { '#', 'e', 'n', 'd', 'i', 'f', ' ', 0 };
const int longEndif = 7;
const char elseD[7] = { '#', 'e', 'l', 's', 'e', ' ', 0 };
const int longElse = 6;

int preprocesar(char* ruta);
int parsear(FILE* miArchivo);
int procesarDirectivas(char* buffer, int* ifs);
int borrarComentarios(char* buffer, int* comentarioAbierto);
int validarCaracteres(char* buffer);
int buscarDefinicion(char* identificador, campoDefine* vector);
int reemplazarDefines(char* buffer, campoDefine* vector);
int obtenerTokens(char* destino, char* source);

int main(int argc, char* argv[]) {
	int resultado;
	if (argc == 2) {
		resultado = preprocesar(argv[1]);
		return (resultado);
	} else {
		printf("%s", "Parámetros incorrectos");
		return (1);
	}
}

/* Recibe un archivo y lo procesa devolviéndolo por pantalla
 *
 */
int preprocesar(char* ruta) {
	FILE* miArchivo;
	miArchivo = fopen(ruta, "r");
	int resultado;
	if (miArchivo != NULL) {
		resultado = parsear(miArchivo);
	} else {
		printf("%s", "Archivo inexistente");
		return 1;
	}
	fclose(miArchivo);
	return (resultado);
}

int parsear(FILE* miArchivo) {
	char buffer[100];
	int comentarioAbierto = 0;
	int resultado = 0;
	int ifs = 0;

	while (fgets(buffer, longitud, miArchivo)) {
		if (borrarComentarios(buffer, &comentarioAbierto) != 0) {
			return 1;
		}
		if (procesarDirectivas(buffer, &ifs) != 0) {
			return 1;
		}
		printf("%s", buffer);
	}
	if (comentarioAbierto) {
		resultado = 1;
	}
	return (resultado);
}

int borrarComentarios(char* buffer, int* comentarioAbierto) {
	int i = 0;
	int tamanio = strlen(buffer);

	while (i + 1 < tamanio) {
		tamanio = strlen(buffer);
		if ((*comentarioAbierto) == 1) {
			int h = i;
			while ((h < tamanio - 1) && ((*comentarioAbierto) == 1)) {
				if (memcmp(cierreDeComentario, buffer + h, 2) == 0) {
					*comentarioAbierto = 0;
				} else {
					h++;
				}
			}
			if ((*comentarioAbierto) == 1) {
				buffer[i] = 0;
			}

			if ((*comentarioAbierto) == 0) {
				strcpy(buffer + i, buffer + h + 2);
			}

		} else if (!(*comentarioAbierto) && (i + 1 < tamanio)) {
			if (buffer[i] == '*') {
				if (buffer[i + 1] == '/')
					return 1;
			} else if (buffer[i] == '/') {
				if (buffer[i + 1] == '*') {
					*comentarioAbierto = 1;
					i--;
				} else if (buffer[i + 1] == '/') {
					buffer[i] = 10;
					buffer[i + 1] = 0;
				}
			}

			i++;
		}
	}
	if ((buffer[0] == saltoDeLinea) && (*comentarioAbierto)) {
		buffer[0] = 0;

	}
	return 0;
}

int procesarDirectivas(char* buffer, int* ifs) {
	static int omitirCodigo = 0;
	int hayDirectiva;
	hayDirectiva = 0;
	static int ultimoDefine = 0;
	static int sobreElse = 0;
	campoDefine vectorDefine[cantidadDeDefines];

	if (strncmp(elseD, buffer, longElse) == 0) {
		hayDirectiva = 1;
		if (sobreElse)
			return 1;
		omitirCodigo = !omitirCodigo;
		sobreElse = 1;
	}

	else if (strncmp(endif, buffer, longEndif) == 0) {
		hayDirectiva = 1;
		omitirCodigo = 0;
		*ifs = (*ifs) - 1;
		sobreElse = 0;
	}

	else if (!(omitirCodigo)) {
		if (strncmp(ifndef, buffer, longIfndef) == 0 || (strncmp(ifdef, buffer, longIfdef) == 0)) {
			hayDirectiva = 1;
			if (*ifs == 15)
				return 1;
			char* identificador;
			char* token;

			char temp[100];
			strcpy(temp, buffer);

			token = strtok(temp, " \n");
			token = strtok(NULL, " \n");
			identificador = token;
			//Mas de una palabra
			if (strtok(NULL, " \n") != NULL) {
				return 1;
			}
			validarCaracteres(identificador);
			int indice = buscarDefinicion(identificador, vectorDefine);

			if (strncmp(ifndef, buffer, longIfndef) == 0) {
				if (indice < 0)
					omitirCodigo = 0;

				else
					omitirCodigo = 1;

			}

			else if (strncmp(ifdef, buffer, longIfdef) == 0) {
				if (indice < 0)
					omitirCodigo = 1;
				else
					omitirCodigo = 0;
			}
		} else if (strncmp(include, buffer, longInclude) == 0) {
			hayDirectiva = 1;
			char archivo[100];
			strcpy(archivo, get_current_dir_name());
			strcat(archivo,"/");
			char* token;
			token = strtok(buffer+longInclude,"\"");
			strcat(archivo, token);

		} else if (strncmp(define, buffer, longDefine) == 0) {
			hayDirectiva = 1;
			if (strlen(buffer) > limiteDefine)
				return 1;
			else {
				if (validarCaracteres(buffer) != 0) {
					return 1;
				} else {
					char* token;
					char temp[100];
					strcpy(temp, buffer);

					token = strtok(temp, " \n");
					token = strtok(NULL, " \n");
					strcpy(vectorDefine[ultimoDefine].nombre, token);
					token = strtok(NULL, "\n");
					if (token != NULL) {
						strcpy(vectorDefine[ultimoDefine].valor, token);
					}

					ultimoDefine++;
				}
			}
		}
	}
	if (hayDirectiva) {
		buffer[0] = finDeLinea;
		buffer[1] = 0;
	} else
		reemplazarDefines(buffer, vectorDefine);
	return 0;
}

int validarCaracteres(char* buffer) {
	return 0;
}

int buscarDefinicion(char* identificador, campoDefine* vector) {
	int i;
	for (i = 0; i < cantidadDeDefines; i++) {
		if (strcmp(identificador, vector[i].nombre) == 0) {
			break;
		}
	}
	if (i == 50)
		i = -1;

	return i;
}

int reemplazarDefines(char* buffer, campoDefine* vector) {
	char token[50];
	char temp[100];
	char resultado[100];
	resultado[0] = 0;
	strcpy(temp, buffer);
	int huboDefinicion = 0;

	int tipoDeToken;
	tipoDeToken = obtenerTokens(token, temp);
	while (tipoDeToken > 0) {
		if (tipoDeToken == 1) {
			int pos = buscarDefinicion(token, vector);
			if (pos >= 0) {
				if (vector[pos].valor[0] != 0) {
					strcpy(token, vector[pos].valor);
					huboDefinicion = 1;
				}
			}
			strcat(resultado, token);
		}
		if (tipoDeToken == 2) {
			strcat(resultado, token);
		}
		tipoDeToken = obtenerTokens(token, temp);
	}

	if (huboDefinicion)
		strcpy(buffer, resultado);
	return 0;
}

/* Esta funcion quita el token mas proximo desde la izquierda, y lo coloca en destino
 * Si retorna 0, la linea esta vacia,
 * 1, retorno un token con caracteres validos
 * 2, retorno un token con caracteres de separaciones
 */
int obtenerTokens(char* destino, char* source) {
	if (source[0] == 0)
		return 0;

	int i = strspn(source, caracteresDeSeparacion);
	if (i == 0) {
		//Caracteres de campo
		int h = strcspn(source, caracteresDeSeparacion);
		strncpy(destino, source, h);
		destino[h] = 0;
		strcpy(source, source + h);
		return 1;
	} else {
		//Caracteres de separacion
		strncpy(destino, source, i);
		destino[i] = 0;
		strcpy(source, source + i);
		return 2;
	}

}
